package com.smasher.backup.dialog.select

import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import androidx.core.widget.doAfterTextChanged
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.paging.LoadState
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager
import com.smasher.backup.dialog.BaseDialogFragment
import com.smasher.backup.dialog.R
import com.smasher.backup.dialog.databinding.FragmentSelectDelegateBinding
import com.smasher.backup.dialog.select.adapter.SelectPageAdapter
import com.smasher.backup.dialog.select.delegate.ElementShowDelegate
import com.smasher.backup.dialog.select.listener.OnSelectListener
import com.smasher.backup.dialog.select.model.TargetBackendService
import com.smasher.backup.dialog.select.model.TargetPagingViewModel
import com.smasher.backup.dialog.select.model.TargetViewModelFactory
import com.smasher.state.StateViewHelper
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch

/**
 * baseSelectDialog
 *
 * @param <T>
 */
abstract class BaseSelectWithApiDialog<T : Any> : BaseDialogFragment(), View.OnClickListener {

    lateinit var mBinding: FragmentSelectDelegateBinding

    private val backendService = object : TargetBackendService() {
        override suspend fun searchUsers(key: String, pageNumber: Int): List<Any> {
            return queryNet(key, pageNumber)
        }
    }
    private val listeners: MutableList<OnSelectListener<T>> = mutableListOf()
    private val factory = TargetViewModelFactory<T>(backendService)
    private val delegate by lazy {
        createShowDelegate()
    }
    private val differ = object : DiffUtil.ItemCallback<T>() {
        override fun areItemsTheSame(p0: T, p1: T): Boolean {
            return areItemsTheSameTarget(p0, p1)
        }

        override fun areContentsTheSame(p0: T, p1: T): Boolean {
            return areContentsTheSameTarget(p0, p1)
        }

    }
    val mAdapter = SelectPageAdapter<T>(delegate, differ)
    val mHelper by lazy { StateViewHelper(mBinding.container) }
    var mode: SelectMode = SelectMode.SINGLE
        set(value) {
            field = value
            mAdapter.mode = value
        }

    var enableSearch: Boolean = true
    val viewModel by viewModels<TargetPagingViewModel<T>> { factory }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        mBinding = FragmentSelectDelegateBinding.inflate(inflater, container, false)
        return mBinding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        mHelper.stateLoading()
        mHelper.onReloadClickListener(reloadListener)

        mAdapter.mode = mode
        mBinding.recyclerView.adapter = mAdapter
        mBinding.recyclerView.layoutManager = LinearLayoutManager(context)

        mBinding.actionConfirm.setOnClickListener(this)
        mBinding.actionBack.setOnClickListener(this)


        mBinding.refreshLayout.setEnableRefresh(true)
        mBinding.refreshLayout.setEnableLoadMore(false)
        mBinding.refreshLayout.setOnRefreshListener {
            mAdapter.refresh()
        }

        initSearch()
        observeFlow()
        loadState()
    }

    private fun initSearch() {
        mBinding.searchContainer.visibility = if (enableSearch) View.VISIBLE else View.GONE
        mBinding.search.doAfterTextChanged { text ->
            viewModel.search(text.toString())
        }
        mBinding.search.setOnEditorActionListener { v, actionId, event ->
            if (actionId == EditorInfo.IME_ACTION_SEARCH) {
                viewModel.search(v.text.toString())
            }
            return@setOnEditorActionListener true
        }
    }

    private fun observeFlow() {
        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.pagingData.collectLatest {
                    Log.d(TAG, "collect: $it")
                    mAdapter.submitData(it)
                }
            }
        }
    }

    private fun loadState() {
        lifecycleScope.launch {
            mAdapter.addLoadStateListener { it ->

                when (it.refresh) {
                    is LoadState.Error -> {
                        mHelper.stateEmpty()
                        Log.d(TAG, "refresh LoadState: Error")
                    }

                    is LoadState.Loading -> {
                        Log.d(TAG, "refresh LoadState: Loading")
                        mHelper.stateLoading()
                    }

                    is LoadState.NotLoading -> {
                        Log.d(TAG, "refresh LoadState: NotLoading")
                        mHelper.stateNormal()
                        if (mBinding.refreshLayout.isRefreshing) {
                            mBinding.refreshLayout.finishRefresh()
                        }
                    }
                }
                when (it.append) {
                    is LoadState.Error -> {
                        Log.d(TAG, "append LoadState: Error")
                        mHelper.stateNormal()
                    }

                    is LoadState.Loading -> {
                        Log.d(TAG, "append LoadState: Loading")
                        mHelper.stateNormal()
                    }

                    is LoadState.NotLoading -> {
                        Log.d(TAG, "append LoadState: NotLoading")
                        mHelper.stateNormal()
                    }
                }

            }
        }
    }

    /**
     * add select listener
     *
     * @param listener
     */
    fun addSelectListener(listener: OnSelectListener<T>) {
        listeners.add(listener)
    }

    /**
     * 获取对话框宽度
     * @return 宽度值，可用 MATCH_PARENT/WRAP_CONTENT 或具体像素值
     */
    abstract fun getWidth(): Int

    /**
     * 获取对话框高度
     * @return 高度值，可用 MATCH_PARENT/WRAP_CONTENT 或具体像素值
     */
    abstract fun getHeight(): Int

    /**
     * DiffUtil 项对比 - 判断是否为同一项
     * @param p0 旧数据项
     * @param p1 新数据项
     * @return true表示是同一项（通常比较唯一标识）
     */
    open fun areItemsTheSameTarget(p0: T, p1: T): Boolean {
        return p0 == p1
    }

    /**
     * DiffUtil 内容对比 - 判断内容是否相同
     * @param p0 旧数据项
     * @param p1 新数据项
     * @return true表示内容相同（比较数据内容字段）
     */
    abstract fun areContentsTheSameTarget(p0: T, p1: T): Boolean

    /**
     * 网络数据请求方法（协程作用域）
     * @return 请求结果数据列表
     */
    abstract suspend fun queryNet(key: String, pageNumber: Int): List<T>

    /**
     * 创建展示代理
     * @return ElementShowDelegate
     */
    abstract fun createShowDelegate(): ElementShowDelegate<T>

    /**
     * reload
     */
    fun reload() {
        mAdapter.refresh()
    }

    /**
     * reload listener
     */
    private val reloadListener = View.OnClickListener { reload() }

    /**
     * Called when a view has been clicked.
     *
     * @param v The view that was clicked.
     */
    override fun onClick(v: View) {
        when (v.id) {
            R.id.action_confirm -> confirmAction()
            else -> dismiss()
        }
    }

    protected open fun confirmAction() {
        val selected = mAdapter.getSelectList()
        if (selected.isEmpty()) return
        Log.d(TAG, "confirmAction: $selected")
        Log.d(TAG, "SelectMode: $mode")
        when (mode) {
            SelectMode.SINGLE -> {
                listeners.forEach { it.onSelectSingle(selected[0]) }
            }

            SelectMode.MULTI -> {
                listeners.forEach { it.onSelectMultiple(selected) }
            }
        }
        dismiss()
    }


    override fun onStart() {
        super.onStart()
        dialog?.window?.setLayout(getWidth(), getHeight())
    }

    override fun onDestroy() {
        super.onDestroy()
        listeners.clear()
    }

}